<?php
namespace app\api\controller;
use app\api\model\Config;
use control\Random;

class Wxsdk
{
    private $cappNum;//小程序资料
    private $sessionKey;

    public function __construct($appNum = 'cnum') {
        $config = new \app\api\model\Config();
        $this->cappNum = $config->getConfigGroup($appNum);
    }

    /**
     * 通过code请求接口获取openid
     * @param $code 用户登录code
     * @return mixed|string
     */
    public function getOpenid($code){
        $url = "https://api.weixin.qq.com/sns/jscode2session?appid={$this->cappNum['client_AppID']}&secret={$this->cappNum['client_AppSecret']}&js_code={$code}&grant_type=authorization_code";
        $result =file_get_contents($url);
        return json_decode($result,true);
    }

    public function getCode($uid, $page, $pid, $group='cnum')
    {
        $confobj = new Config();
        $conf = $confobj->getConfigGroup($group);
        $appid = $conf['client_AppID'];
        $appsecret = $conf['client_AppSecret'];
        $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='.$appid.'&secret='.$appsecret;
        $token = json_decode(file_get_contents($url),true);
        $codeUrl = 'https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token='.$token['access_token'];
        //索引键必须一致
        $data = [
            'scene' => $uid, //二维码传入参数
            'page' => $page, //扫码后进入页面
            'env_version' => 'release', //区别是否跳转正式版和体验版：正式版为 "release"，体验版为 "trial"，开发版为 "develop
        ];
        $json = json_encode($data); //数组加密

        $qr_path = "./upload/";
        if(!file_exists($qr_path.'poster/')){
            mkdir($qr_path.'poster/', 0777,true);//判断保存目录是否存在，不存在自动生成文件目录
        }

        //$filename = 'poster/'.$uid.$pid.'.png';
        $filename = 'poster/'.$uid.'.png';
        $file = $qr_path.$filename;
        file_put_contents($file,$this->http_curl($codeUrl,$json));

        return $file;
    }

    public function getUserPhoto($encryptedData,$iv,$code,$sessionKey){
        if(empty($sessionKey)){
            $url = "https://api.weixin.qq.com/sns/jscode2session?appid={$this->cappNum['client_AppID']}&secret={$this->cappNum['client_AppSecret']}&js_code={$code}&grant_type=authorization_code";
            $res =file_get_contents($url);
            $data = json_decode($res,true);
            $this->sessionKey = $data['session_key'];
        }else{
            $this->sessionKey = $sessionKey;
        }
        $data = $this->decryptData($encryptedData,$iv);

        if(!empty($data->phoneNumber)){
            return $data->phoneNumber;
        }else{
            return 0;
        }
    }

    /**
     * 检验数据的真实性，并且获取解密后的明文.
     * @param $encryptedData string 加密的用户数据
     * @param $iv string 与用户数据一同返回的初始向量
     * @param $data string 解密后的原文
     *
     * @return int 成功0，失败返回对应的错误码
     */
    public function decryptData($encryptedData,$iv)
    {
        if (strlen($this->sessionKey) != 24) {
            return ErrorCode::$IllegalAesKey;
        }
        $aesKey=base64_decode($this->sessionKey);
        if (strlen($iv) != 24) {
            return ErrorCode::$IllegalIv;
        }
        $aesIV=base64_decode($iv);

        $aesCipher=base64_decode($encryptedData);

        $result=openssl_decrypt( $aesCipher, "AES-128-CBC", $aesKey, 1, $aesIV);

        $dataObj=json_decode( $result);
        if( $dataObj  == NULL )
        {
            return -41003;
        }
        if( $dataObj->watermark->appid != $this->cappNum['client_AppID'])
        {
            return -41003;
        }
        return $dataObj;
    }


    //获取预支付参数
    function getWxPrePayId($params)
    {
        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
        try {
            $signParams = array();
            $signParams['appid'] = $this->cappNum['client_AppID'];
            $signParams['mch_id'] =  $this->cappNum['client_MchId'];
            $signParams['nonce_str'] = md5(uniqid());
            $signParams['body'] = mb_substr($params['body'], 0, 50, 'utf-8');//resizeText($params['body'], 64);
            $signParams['out_trade_no'] = $params['paymentId'];
            $signParams['total_fee'] = floatval($params['money']) * 100;
            $signParams['spbill_create_ip'] = $this->getRemoteAddress();
            $signParams['notify_url'] = $params['notifyUrl'];
            $signParams['trade_type'] = $params['tradeType'];
            $signParams['time_start'] = date('YmdHis');
            $signParams['time_expire'] = date('YmdHis', time() + 301);
            if (!empty($params['openId'])) {
                $signParams['openid'] = $params['openId'];
            }
            $sign = $this->createWxMD5Sign($signParams, $this->cappNum['client_Key']);
            $signParams['sign'] = $sign;


            $postData = '<xml>';
            foreach ($signParams as $k => $v) {
                $postData .= '<' . $k . '>' . $v . '</' . $k . '>';
            }
            $postData .= '</xml>';
            $results = $this->http_curl($url,$postData);
            $arr = simplexml_load_string($results, 'SimpleXMLElement', LIBXML_NOCDATA);
            if (empty($arr)) {
                return array('ret' => FALSE, 'data' => '解析Ticket失败：' . $results);
            }
            $arr = (array)$arr;
            if (!isset($arr['return_code']) || $arr['return_code'] != 'SUCCESS') {
                return array('ret' => FALSE, 'data' => '错误：' . $arr['return_code'] . (!empty($arr['return_msg']) ? '，信息：' . $arr['return_msg'] : '') . '，数据：' . print_r($signParams, TRUE));
            }
            if (!isset($arr['result_code']) || $arr['result_code'] != 'SUCCESS') {
                return array('ret' => FALSE, 'data' => '错误：' . $arr['err_code'] . (!empty($arr['err_code_des']) ? '，信息：' . $arr['err_code_des'] : '') . '，数据：' . print_r($signParams, TRUE));
            }
            $data = array();
            $data['trade_type'] = $arr['trade_type'];
            $data['prepay_id'] = $arr['prepay_id'];
            if (!empty($arr['code_url'])) {
                $data['code_url'] = $arr['code_url'];
            }
            if (!empty($arr['mweb_url'])) {
                $data['mweb_url'] = $arr['mweb_url'];
            }
            $data['appId'] = $this->cappNum['client_AppID'];
            $data['partnerKey'] = $this->cappNum['client_Key'];
            return array('ret' => TRUE, 'data' => $data);
        } catch (Exception $e) {
            return array('ret' => FALSE, 'data' => $e->getMessage());
        }
    }

    public function http_curl($url,$arr){
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL,$url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $arr);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);  //不验证 SSL 证书
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);//不验证 SSL 证书域名
        $output = curl_exec($ch);
        curl_close($ch);
        return $output;
    }

    /**
     * 获取ip地址
     *
     * @return mixed ip地址
     */
    function getRemoteAddress()
    {
        $addrs = array();
        if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            foreach (array_reverse(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])) as $x_f) {
                $x_f = trim($x_f);

                if (preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $x_f)) {
                    $addrs[] = $x_f;
                }
            }
        }
        return isset($addrs[0]) ? $addrs[0] : $_SERVER['REMOTE_ADDR'];
    }

    /**
     * 创建微信签名
     *
     * @param array $signParams 签名参数
     * @param string $partnerKey 加密密钥
     * @return string 加密后数据
     */
    function createWxMD5Sign($signParams, $partnerKey)
    {
        if (empty($signParams) || !is_array($signParams)) {
            return '';
        }
        ksort($signParams);
        $str = '';
        foreach ($signParams as $k => $v) {
            if (empty($v)) {
                continue;
            }
            $str .= $k . '=' . $v . '&';
        }
        $str .= 'key=' . $partnerKey;
        return strtoupper(md5($str));
    }

    /**
     * 退款
     * @param float $totalFee 订单金额 单位元
     * @param float $refundFee 退款金额 单位元
     * @param string $refundNo 退款单号
     * @param string $wxOrderNo 微信订单号
     * @param string $orderNo 商户订单号
     * @return string
     */
    public function doRefund($totalFee, $refundFee, $refundNo, $wxOrderNo='',$orderNo='',$refundDesc)
    {
        $config = array(
            'mch_id' => $this->cappNum['client_MchId'],
            'appid' => $this->cappNum['client_AppID'],
            'key' => $this->cappNum['client_Key'],
        );
        $unified = array(
            'appid' => $config['appid'],
            'mch_id' => $config['mch_id'],
            'nonce_str' => Random::alnum(32),
            'total_fee' => intval($totalFee * 100),    //订单金额  单位 转为分
            'refund_fee' => intval($refundFee * 100),    //退款金额 单位 转为分
            'sign_type' => 'MD5',      //签名类型 支持HMAC-SHA256和MD5，默认为MD5
            'transaction_id'=>$wxOrderNo,        //微信订单号
            'out_trade_no'=>$orderNo,    //商户订单号
            'out_refund_no'=>$refundNo,    //商户退款单号
            'refund_desc'=>$refundDesc,   //退款原因（选填）
        );
        $unified['sign'] = $this->getSign($unified, $config['key']);
        $responseXml = $this->curlPost('https://api.mch.weixin.qq.com/secapi/pay/refund', $this->arrayToXml($unified));
        $unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA);
        if ($unifiedOrder === false) {
            return array('return_code'=>'ERROR','msg'=>'parse xml error 订单错误请联系管理员');
        }
        if ($unifiedOrder->return_code != 'SUCCESS') {
            return array('return_code'=>'ERROR','msg'=>$unifiedOrder->return_msg);
        }
        if ($unifiedOrder->result_code != 'SUCCESS') {
            return array('return_code'=>'ERROR','msg'=>$unifiedOrder->err_code_des);
        }
        return array('return_code'=>'SUCCESS','msg'=>'');
    }

    private function arrayToXml($arr)
    {
        $xml = "<xml>";
        foreach ($arr as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
        }
        $xml .= "</xml>";
        return $xml;
    }

    public function getSign($params, $key)
    {
        ksort($params, SORT_STRING);
        $unSignParaString = $this->formatQueryParaMap($params, false);
        $signStr = strtoupper(md5($unSignParaString . "&key=" . $key));
        return $signStr;
    }
    private function formatQueryParaMap($paraMap, $urlEncode = false)
    {
        $buff = "";
        ksort($paraMap);
        foreach ($paraMap as $k => $v) {
            if (null != $v && "null" != $v) {
                if ($urlEncode) {
                    $v = urlencode($v);
                }
                $buff .= $k . "=" . $v . "&";
            }
        }
        $reqPar = '';
        if (strlen($buff) > 0) {
            $reqPar = substr($buff, 0, strlen($buff) - 1);
        }
        return $reqPar;
    }

    public function curlPost($url = '', $postData = '', $options = array())
    {
        if (is_array($postData)) {
            $postData = http_build_query($postData);
        }
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数
        if (!empty($options)) {
            curl_setopt_array($ch, $options);
        }
        //https请求 不验证证书和host
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        //第一种方法，cert 与 key 分别属于两个.pem文件
        //默认格式为PEM，可以注释
        curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
        curl_setopt($ch,CURLOPT_SSLCERT,getcwd().'/../cert/apiclient_cert.pem');
        //默认格式为PEM，可以注释
        curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
        curl_setopt($ch,CURLOPT_SSLKEY,getcwd().'/../cert/apiclient_key.pem');
        //第二种方式，两个文件合成一个.pem文件
//    curl_setopt($ch,CURLOPT_SSLCERT,getcwd().'/all.pem');
        $data = curl_exec($ch);
        curl_close($ch);
        return $data;
    }

    public function getAccessToken() {
        $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->cappNum['client_AppID']}&secret={$this->cappNum['client_AppSecret']}";
        $res = json_decode($this->httpGet($url));
        $access_token = $res->access_token;
        return $access_token;
    }

    public function paramsPush($params,$token){
        $url = 'https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token='.$token;
        $postjson = json_encode($params);
        $resder = $this->http_curl2($url,'post','json',$postjson);
        return $resder;
    }

    public  function http_curl2($url,$type,$res,$arr){
        /*
        $url 请求的url
        $type 请求类型
        $res 返回数据类型
        $arr post请求参数
         */
        $ch=curl_init();
        curl_setopt($ch,CURLOPT_URL,$url);
        curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        if($type=='post'){
            curl_setopt($ch,CURLOPT_POST,1);
            curl_setopt($ch,CURLOPT_POSTFIELDS,$arr);
        }
        $output = curl_exec($ch);
        curl_close($ch);
        if($res=='json'){
            return json_decode($output,true);
        }
    }

    private function httpGet($url) {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_TIMEOUT, 500);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($curl, CURLOPT_URL, $url);

        $res = curl_exec($curl);
        curl_close($curl);

        return $res;
    }


    /**
     * [sendMoney 企业付款到零钱]
     * @param [type] $amount  [发送的金额（分）目前发送金额不能少于1元]
     * @param [type] $re_openid [发送人的 openid]
     * @param string $desc  [企业付款描述信息 (必填)]
     * @param string $check_name [收款用户姓名 (选填)]
     * @return [type]    [description]
     */
    function sendMoney($order_sn,$amount,$re_openid,$desc='用户提现付款'){

        $total_amount = (100) * $amount;
        $data=array(
            'mch_appid'=>$this->cappNum['client_AppID'],//商户账号appid
            'mchid'=> $this->cappNum['client_MchId'],//商户号
            'nonce_str'=>Random::alnum(32),//随机字符串
            'partner_trade_no'=> $order_sn,//商户订单号
            'openid'=> $re_openid,//用户openid
            'check_name'=>'NO_CHECK',//校验用户姓名选项,
            'amount'=>$total_amount,//金额
            'desc'=> $desc,//企业付款描述信息
        );
        //生成签名算法
        $data['sign']=$this->getSign($data,$this->cappNum['client_Key']);
        $data = $this->arrayToXml($data);
        $responseXml = $this->curlPost('https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers',$data);
        $unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA);
        if($unifiedOrder->result_code == 'SUCCESS'){
            return 1;
        }else{
            $error_msg = '系统错误请与管理员联系';
            if(!empty($unifiedOrder->err_code_des)){
                $error_msg = (string)$unifiedOrder->err_code_des;
            }

            return $error_msg;
        }
    }

}

